Skip to content

Remove the old COLOR_MAP and replace with an Enum #342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed

Conversation

leotrs
Copy link
Contributor

@leotrs leotrs commented Aug 25, 2020

Closes #332. Long diff, but self explanatory.

One thing I don't like about the Enum Way (TM) is that this

"colors": [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],

becomes this

"colors": [Colors.blue_d.value, Colors.blue_b.value, Colors.blue_e.value, Colors.grey_brown.value],

which is ugly IMO. But I think overall the Enum makes things nicer in general. What do you think?

EDIT: I tried deprecating the old colors instead of just removing them but it's actually quite the hassle to issue DeprecationWarnings for module-level attributes. So I removed them.


Note 1: the discussion in #332 also mentions converting colors into integers or into a new class. I've left those to a future PR.
Note 2: please do not review docstrings in this PR. Thanks.

@kolibril13
Copy link
Member

I also like the idea of having auto-completion and cleaner implementation of the constants.
However, an everyday manim scene would look like this:

        GraphScene.setup_axes(self)
        # width of edges
        self.x_axis.set_stroke(width=2)
        self.y_axis.set_stroke(width=2)
        # color of edges
        self.x_axis.set_color(RED)
        self.y_axis.set_color(YELLOW)
        # Add x,y labels
        func = TexMobject("\\sin\\theta")
        var = TexMobject("\\theta")
        func.set_color(BLUE)
        var.set_color(PURPLE)
        func.next_to(self.y_axis,UP)
        var.next_to(self.x_axis,RIGHT+UP)
        # Y labels
        self.y_axis.label_direction = LEFT*1.5
        self.y_axis.add_numbers(*[-1,1])
        #Parametters of x labels
        init_val_x = -3*PI/2
        step_x = PI/2
        end_val_x = 3*PI/2

I really like the visual highlighting of things like PI, LEFT, TOP, BLUE, PURPLE, and to easily changing them. Is there maybe a way to use and enums and simultaneously keep BLUE_D instead of Colors.blue_d.value?

@kilacoda-old
Copy link
Contributor

We could create aliases in the file containing the Colors enum:

class Colors(Enum):
    ...

RED = Colors.red
BLUE = Colors.blue
...

@leotrs
Copy link
Contributor Author

leotrs commented Aug 26, 2020

I agree, I kinda hate how it currently looks like with the Colors.blue_c.value. Not great. However, putting them all back as top level constants sort of defeats the purpose of this PR and we might as well close it.

So I say we do one of the following:

  1. Instead of using an Enum, we can have colors be module attributes. So we can do either Colors.WHITE or from Colors import WHITE.

  2. Instead of highlighting the color, we can highlight the module: COLORS.white, or even C.white as an alias.

To be quite honest, this something.value business loos pretty bad to me

@kolibril13
Copy link
Member

With this approach:

# Colors
class Colors(Enum):
    """A list of pre-defined colors."""

    dark_blue = "#236B8E"

DARK_BLUE = Colors.dark_blue.value

we have the highlighting AND auto-completion

@leotrs
Copy link
Contributor Author

leotrs commented Aug 26, 2020

Ok so it seems like this is the state of things:

  1. Con: the current way messes up with auto-completion.
  2. Con: the current way requires us to import all constants at the same time, which is generally bad design.
  3. Pro: the current way produces code that emphasizes the colors because their names are in ALL_CAPS.

This PR is replacing WHITE for Colors.white.value, which fixes the two "cons" but messes up the only "pro".

@kolibril13's suggestion fixes both "con"s and keeps the "pro"! The only part I don't like about it is that if we keep the colors inside the constants.py file then there are only two ways of accessing the colors: from constants import *, in which case you are still polluting the namespace ("con" number 2), or you have to import each color one at a time from constants import WHITE. If you want to import only the colors and not other constants you have to import the Enum and use Colors.white.value, which is ugly and messes up the "pro" number 3.

So I suggest that we keep @kolibril13's suggestion but in a separate file called colors.py. This will fix the two "cons", keep the "pro", AND allow for importing all colors (and only the colors) without importing other constants. You can even do something like import colors as C and use C.WHITE throughout your code. In fact, this is the way I would suggest we adopt this across the whole codebase. It's clean, it allows auto-completion, it emphasizes colors while still being short, it's unambiguous as to where WHITE was defined, and it only brings one name to the namespace when importing it.

EDIT: tagging @PgBiel as he was one of the main supporters of the Enum.

@huguesdevimeux
Copy link
Member

I agree with @kolibril13 .

So I suggest that we keep @kolibril13's suggestion but in a separate file called colors.py. This will fix the two "cons", keep the "pro", AND allow for importing all colors (and only the colors) without importing other constants. You can even do something like import colors as C and use C.WHITE throughout your code. In fact, this is the way I would suggest we adopt this across the whole codebase. It's clean, it allows auto-completion, it emphasizes colors while still being short, it's unambiguous as to where WHITE was defined, and it only brings one name to the namespace when importing it.

This is better IMO.

@huguesdevimeux huguesdevimeux marked this pull request as draft August 27, 2020 21:49
@leotrs
Copy link
Contributor Author

leotrs commented Aug 27, 2020

Just pushed some changes that reflect the discussion above. Please review!

@leotrs leotrs self-assigned this Aug 27, 2020
@leotrs leotrs added the waiting for requested review PR is blocked by a missing requested review label Aug 27, 2020
@huguesdevimeux
Copy link
Member

I have just an issue with that; so If I understood correctly there are two ways to access a color, which are

C.COLOR
and
Color.white.value

Isn't that inconsistent? Both or none of them should use value.

(This is for me non-blocking),

@huguesdevimeux huguesdevimeux added the pr:easy review There is nothing particular (i.e, it's about a general/small thing) to know for review! label Aug 27, 2020
@leotrs leotrs marked this pull request as ready for review August 27, 2020 23:54
@leotrs
Copy link
Contributor Author

leotrs commented Aug 27, 2020

Thanks for taking a look.

I agree it's inconsistent. In my mind, I think we should just support C.WHITE, and nothing else. We can do this by making the Colors Enum private, i.e. renaming it as _Colors or something like that.

# Therefore, we also add each color as a global constant, in upper case, to the
# locals() of this module
COLOR_MAP = {n.upper(): c.value for n, c in Colors.__members__.items()}
locals().update(COLOR_MAP)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this approach, unfortunately, we don't get auto-completion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no! I didn't realize that. Thanks for catching that

@kolibril13
Copy link
Member

kolibril13 commented Aug 28, 2020

In colors, line 124 is nice and compact, however, with this approach, we will lose autocompletion and (at least my) IDE throws a red warning. Note that the scene is still running properly.
abc
So I would change line 124 explicitly to

WHITE = Colors.white.value
....

Then we have auto-completion and no warnings

@huguesdevimeux
Copy link
Member

I think this closes #81

@leotrs
Copy link
Contributor Author

leotrs commented Aug 28, 2020

So I would change line 124 explicitly to

WHITE = Colors.white.value
....

Then we have auto-completion and no warnings

If we do this, then I don't see a reason to use Enum at all.

Tagging @PgBiel who was one of the main supporters of Enum. Thoughts?

@leotrs
Copy link
Contributor Author

leotrs commented Aug 28, 2020

Marking as draft as this needs more discussion before it's merged.

@leotrs leotrs marked this pull request as draft August 28, 2020 11:22
@huguesdevimeux huguesdevimeux removed pr:easy review There is nothing particular (i.e, it's about a general/small thing) to know for review! waiting for requested review PR is blocked by a missing requested review labels Aug 28, 2020
@huguesdevimeux huguesdevimeux linked an issue Sep 1, 2020 that may be closed by this pull request
@leotrs
Copy link
Contributor Author

leotrs commented Sep 2, 2020

So this needs a bit of rethinking. Anybody have any thoughts?

@leotrs
Copy link
Contributor Author

leotrs commented Sep 29, 2020

Closing in favor of #488

@leotrs leotrs closed this Sep 29, 2020
@behackl behackl deleted the colors-enum branch October 22, 2020 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Using classmethods for constants
4 participants